<?php
// @formatter:off
/**
 * @file Class.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-07-05 14:30:06 +0000 (Fri, 05 Jul 2013) $
 *
 * $Id: Class.php 89 2013-07-05 14:30:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0-BETA2/extensions/DirectoryOrganizer/Class.php $
 */

/**
 * Adding all extension's hooks
 * @{
 */
define("WC_E_DIRECTORY_ORGANIZER_VERSION",	"1.0");
define("WC_E_DIRECTORY_ORGANIZER_URI",		substr(dirname(__FILE__), strlen($_SERVER["DOCUMENT_ROOT"])));
define("WC_E_DIRECTORY_ORGANIZER_CONTROLERS",	dirname(__FILE__)."/controlers");
define("WC_E_DIRECTORY_ORGANIZER_TEMPLATES",	dirname(__FILE__)."/templates");

$extensionBasicInfo = array(
	"author"	=> "Alejandro Dario Simi",
	"version"	=> WC_E_DIRECTORY_ORGANIZER_VERSION,
	"url"		=> array(
		"title"		=> "wcomix:Extension:DirectoryOrganizer",
		"href"		=> "http://wiki.daemonraco.com/wiki/Wcomix:Extension:DirectoryOrganizer"
	)
);
$wcCanvasActions["dor.edit"] = array_merge(array(
	"name"		=> "Directory Organizer: Action Edit",
	"description"	=> "Directory Organizer extention: Action Editor",
	"manager"	=> WC_E_DIRECTORY_ORGANIZER_CONTROLERS."/c.edit.php",
	"template"	=> WC_E_DIRECTORY_ORGANIZER_TEMPLATES."/c.edit.html",
	"pre-js"	=> WC_E_DIRECTORY_ORGANIZER_TEMPLATES."/c.dor.js"
), $extensionBasicInfo);
$wcCanvasActions["dor.create"] = array_merge(array(
	"name"		=> "Directory Organizer: Action Create",
	"description"	=> "Directory Organizer extention: Action Creator",
	"manager"	=> WC_E_DIRECTORY_ORGANIZER_CONTROLERS."/c.create.php",
	"template"	=> WC_E_DIRECTORY_ORGANIZER_TEMPLATES."/c.create.html",
	"pre-js"	=> WC_E_DIRECTORY_ORGANIZER_TEMPLATES."/c.dor.js"
), $extensionBasicInfo);
$wcCanvasActions["dor.remove"] = array_merge(array(
	"name"		=> "Directory Organizer: Action Remove",
	"description"	=> "Directory Organizer extention: Action Remover",
	"manager"	=> WC_E_DIRECTORY_ORGANIZER_CONTROLERS."/c.remove.php",
	"template"	=> WC_E_DIRECTORY_ORGANIZER_TEMPLATES."/c.remove.html",
	"pre-js"	=> WC_E_DIRECTORY_ORGANIZER_TEMPLATES."/c.dor.js"
), $extensionBasicInfo);

$auxURI = $wcPaths['extensions-directory-uri'].substr(dirname(__FILE__), strlen($wcPaths['extensions-directory']));
$auxDIR = substr(dirname(__FILE__), strlen($wcROOTDIR));
$wcRuleHooks[DirectoryOrganizer::$TYPE] = array_merge($extensionBasicInfo, array(
		"name"		=> "Directory Organizer",
		"description"	=> "Organize directories tags and contents",

		"class"		=> "DirectoryOrganizer",
		"conficts"	=> "DirectoryOrganizer::MarkConflicts",
		"lock-tags"	=> "DirectoryOrganizer::LockedTagIds",

		//
		// Canvas hooks
		"create"	=> "dor.create",
		"edit"		=> "dor.edit",
		"remove"	=> "dor.remove"
));
$wcDatabase["tables"][] = "{$auxDIR}/db/wc_acts_dor.tab.xml";
$wcDatabase["indexes"][] = "{$auxDIR}/db/wc_acts_dor.idx.xml";

$wcCronProfiles["DOR"] = array(
	"rules.run",
	"lists.update",
	"previews.update"
);
// @formatter:on
/* @} */

class DirectoryOrganizer extends WCRuleAction {
	public static $TYPE = "DOR";

	public static $ModeMove = "MOVE";
	public static $ModeCMove = "CMOVE";
	public static $ModeTagger = "TAGGER";
	public static $ModeCollector = "COLLECTOR";
	public static $ModeVMove = "VMOVE";

	public static $StatusEnabled = "E";
	public static $StatusDisabled = "D";

	protected $_tags = array();
	protected $_directories = array();
	protected $_collections = array();

	//
	// Public methods.
	public function collection() {
		$out = null;
		switch(count($this->_collections)) {
			case 0:
				break;
			case 1:
				$key = array_keys($this->_collections);
				$key = array_shift($key);
				$out = $this->_collections[$key];
				break;
			default:
				$this->setConflict(true);
				break;
		}
		return $out;
	}
	public function directory() {
		$out = null;
		switch(count($this->_directories)) {
			case 0:
				break;
			case 1:
				$key = array_keys($this->_directories);
				$key = array_shift($key);
				$out = $this->_directories[$key];
				break;
			default:
				$this->setConflict(true);
				break;
		}
		return $out;
	}
	public function reload() {
		$this->_tags = array();
		$this->_directories = array();
		$this->_collections = array();

		parent::reload();
	}
	public function run() {
		$out = array();

		if($this->status == self::$StatusEnabled) {
			switch($this->mode) {
				case self::$ModeMove:
					$out = $this->runModeMove();
					break;
				case self::$ModeCMove:
					$out = $this->runModeCMove();
					break;
				case self::$ModeTagger:
					$out = $this->runModeTagger();
					break;
				case self::$ModeCollector:
					$out = $this->runModeCollector();
					break;
				case self::$ModeVMove:
					$out = $this->runModeVMove();
					break;
			}
		} else {
			$out = array("items" => array( array(
						"action" => "Action '{$this->name()}' ({$this->_id}@{$this->_ruleId}) is disabled",
						"member" => false,
						"status" => false,
						"fatal" => false
					)));
		}

		return $out;
	}
	public function tags() {
		return $this->_tags;
	}
	public function words() {
		$out = array();

		foreach($this->_tags as $item) {
			$out = array_merge($out, WCSearchFactory::StringToPatterns($item->name));
		}
		foreach($this->_directories as $item) {
			$out = array_merge($out, WCSearchFactory::StringToPatterns($item->name));
		}
		foreach($this->_collections as $item) {
			$out = array_merge($out, WCSearchFactory::StringToPatterns($item->name));
		}

		return $out;
	}
	//
	// Protected methods.
	protected function enforceMode() {
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "update  {$this->_dbprefix}acts_dor\n";
			$query .= "set     act_mode = :mode\n";
			$query .= "where   act_rule   = :rule\n";
			$query .= " and    act_group  = :group\n";
			$query .= " and    act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id,
			":mode" => $this->mode
		);
		$stmt->execute($params);
	}
	protected function load() {
		if($this->_type != self::$TYPE) {
			trigger_error("Loaded type and class type are different.", E_USER_ERROR);
		}

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[1]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}acts_dor\n";
			$query .= "where   act_rule   = :rule\n";
			$query .= " and    act_group  = :group\n";
			$query .= " and    act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id
		);
		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			global $wcTagsHolder;
			global $wcDirectoriesHolder;
			global $wcCollectionsHolder;

			foreach($stmt->fetchAll() as $row) {
				if($row["act_tag"] && !isset($this->_tags[$row["act_tag"]])) {
					$tag = $wcTagsHolder->item($row["act_tag"], $this->_group);
					if($tag && $tag->ok()) {
						$this->_tags[$tag->id] = $tag;
					}
				}

				if($row["act_directory"] && !isset($this->_tags[$row["act_directory"]])) {
					$directory = $wcDirectoriesHolder->item($row["act_directory"], $this->_group);
					if($directory && $directory->ok()) {
						$this->_directories[$directory->id] = $directory;
					}
				}
				$this->setConflict($this->_conflict || count($this->_directories) > 1);

				if($row["act_collection"] && !isset($this->_tags[$row["act_collection"]])) {
					$collection = $wcCollectionsHolder->item($row["act_collection"], $this->_group);
					if($collection && $collection->ok()) {
						$this->_collections[$collection->id] = $collection;
					}
				}
				$this->setConflict($this->_conflict || count($this->_collections) > 1);

				if($row["act_mode"] != $this->mode) {
					$this->enforceMode();
				}
			}
		}

		$this->_ok = true;

		if(!$this->mode) {
			$this->mode = self::$ModeTagger;
		}
		if(!$this->status) {
			$this->status = self::$StatusEnabled;
		}
	}
	protected function moveTo(WCComic &$comic, WCDirectory &$auxDir) {
		$aux = array(
			"action" => "Moving '{$comic->name}' ({$comic->id}) from '{$comic->directory()->name}' ({$comic->directory}) to tag '{$auxDir->name}' ({$auxDir->id})",
			"member" => $comic,
			"fatal" => true
		);

		$futurePath = WCSanitizer::SystemPath("{$auxDir->name}/{$comic->name}");
		if(!file_exists($futurePath)) {
			$aux["status"] = $comic->setDirectory($auxDir);
		} else {
			$aux["status"] = false;
			$aux["error"] = "Path '{$futurePath}' already exists";
		}

		return $aux;
	}
	protected function removeSpecific() {
		$out = true;

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}acts_dor\n";
			$query .= "where       act_rule   = :rule\n";
			$query .= " and        act_group  = :group\n";
			$query .= " and        act_action = :id\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":rule" => $this->_ruleId,
			":group" => $this->_group->id,
			":id" => $this->_id
		);

		if(!$stmt->execute($params)) {
			$out = false;
		}

		return $out;
	}
	protected function runModeCMove() {
		$out = array();

		if($this->ok() && !$this->conflict() && $this->directory()) {
			global $wcTagsHolder;

			if($this->directory() && $this->directory()->ok() && $this->collection() && $this->collection()->ok()) {
				$auxDir = $this->directory();
				$auxCol = $this->collection();
				//
				// Checking every comic of the collection.
				foreach($auxCol->comics() as $comic) {
					//
					// Checking if the selected comic
					// is not in the directory.
					if($comic->directory != $auxDir->id) {
						if(!isset($out["items"])) {
							$out["items"] = array();
						}
						//
						// It seems not to be present,
						// therefore it is moved.
						$out["items"][] = $this->moveTo($comic, $auxDir);
					}
				}
			}
		}

		return $out;
	}
	protected function runModeCollector() {
		$out = array();

		if($this->ok() && !$this->conflict() && $this->directory()) {
			global $wcTagsHolder;

			$idMembers = array();

			if($this->directory() && $this->directory()->ok()) {
				$collection = $this->collection();
				//
				// Checking every comic in the directory.
				foreach($this->directory()->membersFull() as $comic) {
					//
					// Checking comic collection.
					if($comic->collection != $collection->id) {
						$comic->collection = $collection->id;

						$aux = array(
							"action" => "Adding '{$comic->name}' ($comic->id@$comic->directory) to collection '{$collection->name}' ({$collection->id})",
							"member" => $comic,
							"status" => true,
							"fatal" => true
						);
						if(!isset($out["items"])) {
							$out["items"] = array();
						}
						$out["items"][] = $aux;
					}
				}
			}
		}

		return $out;
	}
	protected function runModeMove() {
		$out = array();

		if($this->ok() && !$this->conflict() && $this->directory()) {
			global $wcTagsHolder;

			if($this->directory() && $this->directory()->ok()) {
				$auxDir = $this->directory();
				//
				// Checking every tag.
				foreach($this->tags() as $tag) {
					//
					// Checking every comic of the current
					// tag.
					foreach($tag->membersFull() as $comic) {
						//
						// Checking if the selected comic
						// is not in the directory.
						if($comic->directory != $auxDir->id) {
							if(!isset($out["items"])) {
								$out["items"] = array();
							}
							//
							// It seems not to be
							// present, therefore it
							// is moved.
							$out["items"][] = $this->moveTo($comic, $auxDir);
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeTagger() {
		$out = array();

		if($this->ok() && !$this->conflict() && $this->directory()) {
			global $wcTagsHolder;

			$idMembers = array();

			if($this->directory() && $this->directory()->ok()) {
				//
				// Checking every comic in the directory.
				foreach($this->directory()->membersFull() as $comic) {
					//
					// Checking tags in the comic.
					foreach($this->tags() as $tag) {
						//
						// Caching comics ids to gain
						// performance.
						if(!isset($idMembers[$tag->id])) {
							$idMembers[$tag->id] = $tag->members();
						}
						//
						// Checking if the selected comic
						// has the tag.
						if(!in_array($comic->id, $idMembers[$tag->id])) {
							//
							// It seems not to be
							// present, therefore it
							// is added.
							$aux = array(
								"action" => "Adding '{$comic->name}' ($comic->id@$comic->directory) to tag '{$tag->name}' ({$tag->id})",
								"member" => $comic,
								"status" => true,
								"fatal" => true
							);
							$tag->add($comic);
							if(!isset($out["items"])) {
								$out["items"] = array();
							}
							$out["items"][] = $aux;

							unset($idMembers[$tag->id]);
						}
					}
				}
			}
		}

		return $out;
	}
	protected function runModeVMove() {
		$out = array();

		if($this->ok() && !$this->conflict() && $this->directory()) {
			global $wcTagsHolder;

			if($this->directory() && $this->directory()->ok()) {
				$auxDir = $this->directory();
				//
				// Checking every tag.
				foreach($this->tags() as $tag) {
					//
					// Checking every comic of the current
					// tag.
					foreach($tag->membersFull() as $comic) {
						$dontRemove = false;

						if(!isset($out["items"])) {
							$out["items"] = array();
						}
						//
						// Checking if the selected comic
						// is not in the directory.
						if($comic->directory != $auxDir->id) {
							//
							// It seems not to be
							// present, therefore it
							// is moved.
							$aux = $this->moveTo($comic, $auxDir);
							$out["items"][] = $aux;
							$dontRemove = !$aux["status"];
						}
						//
						// Tag was already check,
						// therefore, it is removed.
						if(!$dontRemove) {
							$aux = array(
								"action" => "Removing '{$comic->name}' ({$comic->id}@{$comic->directory}) from tag '{$tag->name}' ({$tag->id})",
								"member" => $comic,
								"fatal" => true
							);
							$aux["status"] = $tag->removeComic($comic);
							$out["items"][] = $aux;
						}
					}
				}
			}
		}

		return $out;
	}
	//
	// Class methods.
	public static function LockedTagIds(WCGroup &$group) {
		$out = array();

		global $wcDatabaseAccess;

		$db = WCConnection::Instance();
		$dbprefix = $wcDatabaseAccess['prefix'];

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($db->has($stmtId)) {
			$stmt = $db->get($stmtId);
		} else {
			$query = "select  distinct(act_tag) as tag\n";
			$query .= "from    {$dbprefix}acts_dor\n";

			$stmt = $db->prepare($stmtId, $query);
		}

		if($stmt->execute()) {
			foreach($stmt->fetchAll() as $row) {
				$out[] = $row["tag"];
			}
		}

		return array_unique($out);
	}
	public static function MarkConflicts() {
		global $wcDatabaseAccess;

		$out = 0;

		$db = WCConnection::Instance();
		$dbprefix = $wcDatabaseAccess['prefix'];
		//
		// Looking for tags that move contents to two different
		// directories.
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[MOVE-MULTI_DIR]";
		if($db->has($stmtId)) {
			$stmt = $db->get($stmtId);
		} else {
			$query = "update  {$dbprefix}rule_actions\n";
			$query .= "set     rel_ra_conflict = 'Y'\n";
			$query .= "where   (rel_ra_rule,rel_ra_action) in (\n";
			$query .= "                select  act_rule,\n";
			$query .= "                        act_action\n";
			$query .= "                from    {$dbprefix}acts_dor\n";
			$query .= "                where   act_tag in (\n";
			$query .= "                                select  act_tag\n";
			$query .= "                                from    {$dbprefix}acts_dor\n";
			$query .= "                                where   act_mode = 'MOVE'\n";
			$query .= "                                group by act_tag\n";
			$query .= "                                having count(distinct(act_directory)) > 1))\n";

			$stmt = $db->prepare($stmtId, $query);
		}

		if($stmt->execute()) {
			$out += $stmt->rowCount();
		}
		//
		// Looking for collections that move contents to two different
		// directories.
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[CMOVE-MULTI_DIR]";
		if($db->has($stmtId)) {
			$stmt = $db->get($stmtId);
		} else {
			$query = "update  {$dbprefix}rule_actions\n";
			$query .= "set     rel_ra_conflict = 'Y'\n";
			$query .= "where   (rel_ra_rule,rel_ra_action) in (\n";
			$query .= "                select  act_rule,\n";
			$query .= "                        act_action\n";
			$query .= "                from    {$dbprefix}acts_dor\n";
			$query .= "                where   act_collection in (\n";
			$query .= "                                select  act_collection\n";
			$query .= "                                from    {$dbprefix}acts_dor\n";
			$query .= "                                where   act_mode = 'CMOVE'\n";
			$query .= "                                group by act_collection\n";
			$query .= "                                having count(distinct(act_directory)) > 1))\n";

			$stmt = $db->prepare($stmtId, $query);
		}

		if($stmt->execute()) {
			$out += $stmt->rowCount();
		}

		return $out;
	}
}
?>